package com.mainService.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.alibaba.dubbo.config.annotation.Reference;
import com.alibaba.excel.support.ExcelTypeEnum;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dubboInterface.merchant.chartHandleInterface.ExcelRead;
import com.dubboInterface.merchant.chartHandleInterface.ExcelWrite;
import com.dubboInterface.merchant.crawleServiceInterface.shanxiInterface.ShanxiBaseClick;
import com.dubboInterface.merchant.fileStorageInterface.FileStore;
import com.mainService.bean.ComprehensiveDownload;
import com.mainService.mapper.SmokeInfoLogMapper;
import com.mainService.mapper.SmokeMapper;
import com.mainService.service.PicService;
import com.mainService.service.SmokeInfoLogService;
import com.mainService.service.SmokeService;
import com.projectCommon.bean.*;
import com.projectCommon.bean.constant.Constant;
import com.projectCommon.util.springUtil.annotation.MethodEventDescription;
import com.projectCommon.bean.dto.SmokeDto;
import com.projectCommon.bean.easyExcel.CSVSmokeInfo;
import com.projectCommon.bean.easyExcel.ExcelSmokeInfo;
import com.projectCommon.bean.easyExcel.putTactics.AnticipatedOrderData;
import com.projectCommon.bean.easyExcel.putTactics.GearExtendSelection;
import com.projectCommon.bean.easyExcel.putTactics.PriceSelection;
import com.projectCommon.bean.easyExcel.putTactics.WeeklySupplyStrategyData;
import com.projectCommon.bean.httpSmokeBean.shanxitobaccobean.ThreePublishBaseInfo;
import com.projectCommon.bean.httpSmokeBean.shanxitobaccobean.ThreePublishRes;
import com.projectCommon.bean.httpSmokeBean.shanxitobaccobean.response.Bill;
import com.projectCommon.bean.httpSmokeBean.shanxitobaccobean.response.OrderInfo;
import com.projectCommon.bean.httpSmokeBean.shanxitobaccobean.response.OrderingInformation;
import com.projectCommon.bean.httpSmokeBean.shanxitobaccobean.response.ProdLimitInfos;
import com.projectCommon.bean.result.CODE;
import com.projectCommon.bean.result.CheckInfo;
import com.projectCommon.bean.result.CheckInfoRes;
import com.projectCommon.bean.result.SmokeNews;
import com.projectCommon.excetion.PicNullException;
import com.projectCommon.excetion.SmokeException;
import com.projectCommon.util.commonUtil.*;
import com.projectCommon.util.threadSave.ThreadUserInfo;
import lombok.extern.slf4j.Slf4j;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

/**
 * @author 吴宇~
 * @motto 我亦无他，唯手熟尔。
 * @edition jdk1.8
 * @reflection 0-1 喧闹任其喧闹，自由我自为之，我自风情万种，与世无争。
 * @date 2024/04/04/20:48
 * @target 心若有所向往，何惧道阻且长
 */

/**
 * 这个实现和SmokeInfoService 的区别是  。
 * 这个实现重在数据库和业务上的操作 ，那个是实现重在封装http请求的操作
 */
@Service
@Slf4j
public class SmokeServiceImpl extends ServiceImpl<SmokeMapper, Smoke> implements SmokeService {
    @Reference
    private ShanxiBaseClick shanxiBaseClick;
    @Reference
    private ExcelRead excelRead;
    @Reference
    private ExcelWrite excelWrite;
    @Reference
    private FileStore fileStore;

    @Autowired
    private SmokeService smokeService;
    @Autowired
    private SmokeMapper smokeMapper;
    @Autowired
    private SmokeInfoLogMapper smokeInfoLogMapper;
    @Autowired
    private SmokeInfoLogService smokeInfoLogService;
    @Autowired
    private PicService picService;

    //得到最新订单所需要的list 已map的方式 呈现
    @MethodEventDescription(event = "get all excel list Map")
    @Override
    public Map<String, Object> getExcelListMap(List<CSVSmokeInfo> baseSmokeInfos) {
        HashMap<String, Object> excelListMap = new HashMap<>();

        List<SmokeDto> allSmokeBatchInfo = new ArrayList<>();
        List<SmokeDto> addSmokeDtos = new ArrayList<>();
        List<SmokeDto> deleteSmokeDtos = new ArrayList<>();
        List<SmokeDto> updateSmokeDtos = new ArrayList<>();
        String latestSerialId = smokeMapper.getLatestSerialId();
        //如果序列号为空 一般不会
        if (latestSerialId == null) {
            // 说明原本没有数据
            throw new RuntimeException("序列号异常");
        }
        List<String> allSerialDesc = smokeMapper.getAllSerialDesc();
        do {
            List<SmokeDto> allSmoke = smokeMapper.getAllSmokeBySerial(latestSerialId);
            allSmokeBatchInfo.addAll(allSmoke);
            latestSerialId = ListUtil.getPreSerialId(allSerialDesc, latestSerialId);
            if (latestSerialId == null) {
                //如果没有就说明 没有更多的信息了。
                break;
            }
        } while (allSmokeBatchInfo.size() < 10);
        for (SmokeDto smoke : allSmokeBatchInfo) {
            char dataStatus = smoke.getDataStatus();
            if (dataStatus == '1') {
                String orgIsCigar = smoke.getOrgIsCigar();
                String orgIsAbnormity = smoke.getOrgIsAbnormity();

                String isCigar = "1".equals(orgIsCigar) ? "是" : "0".equals(orgIsCigar) ? "不是" : "未知";
                String isAbnormity = "1".equals(orgIsAbnormity) ? "是" : "0".equals(orgIsAbnormity) ? "不是" : "未知";

                String remarks = String.format("%s|上架了 %s|批发价: %s|建议售卖价: %s|%s 异型|%s 雪茄|%s",
                        DateUtil.formatTime(smoke.getCreateTime()),
                        smoke.getProductName(),
                        smoke.getWholeSalePrice(),
                        smoke.getRetailPrice(),
                        isAbnormity,
                        isCigar,
                        smoke.getPriceTypeCodeName());
                String s = StringUtils.formatString(remarks, new int[]{22, 23, 18, 20, 7, 10, 5});
                SmokeDto smokeDto = new SmokeDto();
                smokeDto.setChangeDesc(s);
                if (smoke.getPicAddress() != null) {
                    File file = fileStore.fetchFile(smoke.getPicAddress() + "." + smoke.getImageType());
                    smokeDto.setPicFile(file);
                }
                addSmokeDtos.add(smokeDto);
            } else if (dataStatus == '2') {
                //下架
                String format = String.format("%s|下架了 %s",
                        DateUtil.formatTime(smoke.getCreateTime()),
                        smoke.getProductName());
                String desc = StringUtils.formatString(format, new int[]{22, 20});
                SmokeDto smokeDto = new SmokeDto();
                smokeDto.setChangeDesc(desc);
                if (smoke.getPicAddress() != null) {
                    File file = fileStore.fetchFile(smoke.getPicAddress() + "." + smoke.getImageType());
                    smokeDto.setPicFile(file);
                }
                deleteSmokeDtos.add(smokeDto);
            } else if (dataStatus == '3') {
                String format = String.format("%s|%s 发生更新|更新有：..更新为..", smoke.getCreateTime(), smoke.getProductName());
                SmokeDto smokeDto = new SmokeDto();
                String remark = StringUtils.formatString(format, new int[]{22, 22, 30});
                smokeDto.setChangeDesc(remark);
                if (smoke.getPicAddress() != null) {
                    File file = fileStore.fetchFile(smoke.getPicAddress() + "." + smoke.getImageType());
                    smokeDto.setPicFile(file);
                }
                updateSmokeDtos.add(smokeDto);
            }
        }
        if (addSmokeDtos.size() == 0) {
            SmokeDto smokeDto = new SmokeDto();
            smokeDto.setChangeDesc("无上架");
            addSmokeDtos.add(smokeDto);
        }

        if (deleteSmokeDtos.size() == 0) {
            SmokeDto smokeDto = new SmokeDto();
            smokeDto.setChangeDesc("无下架");
            deleteSmokeDtos.add(smokeDto);
        }

        if (updateSmokeDtos.size() == 0) {
            SmokeDto smokeDto = new SmokeDto();
            smokeDto.setChangeDesc("无修改");
            updateSmokeDtos.add(smokeDto);
        }
        excelListMap.put("addSmokeRemarks", addSmokeDtos);
        excelListMap.put("deleteSmokeRemarks", deleteSmokeDtos);
        excelListMap.put("updateSmokeRemarks", updateSmokeDtos);
        return excelListMap;
    }

    // 得到周货源投放策略的信息的图片
    @Override
    public byte[] getWeeklySupplyPlacementStrategyPicInformation() {
        // 首先获取excel的信息。然后转换位图片
        byte[] weeklySupplyPlacementStrategyExcelInformation = getWeeklySupplyPlacementStrategyExcelInformation();//        4. 将excel转换为图片输出
        return smokeService.excelToPic(weeklySupplyPlacementStrategyExcelInformation);
    }

    /**
     * 得到周货源投放策略 excel 表格信息
     *
     * @return
     */
    @Override
    public byte[] getWeeklySupplyPlacementStrategyExcelInformation() {
//        1. 获取模板数据
//          1.1 首先点击更多
        ThreePublishRes threePublishRes = shanxiBaseClick.threeOpenness_more();
//          1.2 然后选择需要的数据进行下载，
        List<ThreePublishBaseInfo> rows = threePublishRes.getRows();
        String targetHref = null;
        String fileName = null;
        for (ThreePublishBaseInfo row : rows) {
            Elements elements = Jsoup.parse(row.getRemark()).select("a[href]");
            if (elements.size() == 0) {
                continue;
            }
            Element element = elements.get(0);
            fileName = element.text();
            String regex = "\\d{4}\\.([1-9]|1[0-2])\\.([1-9]|[12][0-9]|3[01])-" + // 开始日期部分
                    "([1-9]|1[0-2])\\.([1-9]|[12][0-9]|3[01])" + // 结束日期部分
                    "周货源投放策略\\(太原\\)\\.xls[x]?"; // 文件名后缀
            Matcher matcher = Pattern.compile(regex).matcher(fileName);
            if (matcher.matches()) {
                targetHref = element.attr("href");
                log.debug("选择的数据为: {}", fileName);
                break;
            }
        }
        byte[] bytes = shanxiBaseClick.downPublicInformation(targetHref);
//        2. 解析数据  最终得到一个对象，包含全部的数据
        WeeklySupplyStrategyData weeklySupplyStrategyData = excelRead.readWeeklySupplyStrategyData(bytes, fileName);
//            2.1 处理这个数据，将不投放的卷烟去掉
        revisedData(weeklySupplyStrategyData);
//        3. 将数据根据模板写入  这部分应该也是easyExcel 来处理的事情。但是需要使用继续查询数据库的数据，所以在这儿查好传过去
        List<String> encodeList = new ArrayList<>();
        for (AnticipatedOrderData anticipatedOrderData : weeklySupplyStrategyData.getGearSelection().getList()) {
            encodeList.add(anticipatedOrderData.getProductEncoding());
        }

        for (PriceSelection selection : weeklySupplyStrategyData.getPriceSelection()) {
            for (AnticipatedOrderData anticipatedOrderData : selection.getList()) {
                encodeList.add(anticipatedOrderData.getProductEncoding());
            }
        }
        for (GearExtendSelection gearExtendSelection : weeklySupplyStrategyData.getGearExtendSelectionList()) {
            encodeList.add(gearExtendSelection.getList().get(0).getProductEncoding());
        }
        List<Smoke> allSmokeByProductCode = smokeMapper.getAllSmokeByProductCode(encodeList);
        Map<String, Smoke> dbInfo = allSmokeByProductCode.stream().collect(Collectors.toMap(Smoke::getProductCode, smoke -> smoke));
        return excelWrite.weeklySupplyStrategyWrite(ExcelTypeEnum.XLS, weeklySupplyStrategyData, dbInfo);
    }

    @Override
    public byte[] getWeeklySupplyPlacementStrategyPicInformationByCsv(String fileType, byte[] csvByte, String fileName) {
        return new byte[0];
    }

    @Override
    public byte[] comprehensiveDownload(ComprehensiveDownload comprehensiveDownload) {
        String interfaceName = comprehensiveDownload.getInterfaceName();
//        1.首先判断是否有这个接口，如果没有这个接口 直接返回null，错误
        if (!Constant.SMOKE_INTERFACE_LIST.contains(interfaceName)) {
            log.error("没有这个接口: {}", interfaceName);
            return null;
        }
//        2.如果有这个接口，就根据接口名称进行不同的操作
//        2.1 首先将base64进行解码
        String content = comprehensiveDownload.getContent();
        byte[] fileBytes = Base64Util.decode(content);
        comprehensiveDownload.setFile(fileBytes);
//        2.2 然后根据接口名称进行不同的操作


        return new byte[0];
    }

    /**
     * 下载图片，得到最新的订单的图片byte数组
     *
     * @return 经过转换的订单的 byte数组
     */
    @Override
    @MethodEventDescription(event = "use {{#1}} template get latest order pic", isFillMethodArg = true, isShowAllArg = true, isShowAllEventCost = true)
    public byte[] getLastOrderPic(String template) {
        // 1. 计算一个获取订单信息的开始和结束时间
        String currentQuarterFirstDay = DateUtil.queryCurrentQuarterFirstDay();
        String endDay = DateUtil.queryCurrentDay();
        String startDay = currentQuarterFirstDay.equals(endDay) ? DateUtil.getLastQuarterFirstDay() : currentQuarterFirstDay;
        // 2. 根据开始结束时间 得到订单信息选择一个
        OrderInfo orderInfo = shanxiBaseClick.selectOrderListByDate(startDay, endDay);
        if (orderInfo.getRows().size() == 0 || orderInfo.getRows().get(0) == null) {
            System.out.println("未查询到对应的订单信息");
        }
        Bill bill = orderInfo.getRows().get(0);
        // 3. 根据选择的订单的id进行爬整个数据
        byte[] bytes = shanxiBaseClick.selectOrderDetailCSVFileByOrderId(bill.getBill_uuid());
        // 4. 读取这个数据 读取为一个list集合对象
        List<CSVSmokeInfo> smokeInfoList = excelRead.readOrderCsvFileInfo(bytes);
        // 5. 根据读取的数据转换为实际的订购数据格式对象
        List<ExcelSmokeInfo> excelSmokeInfo = smokeService.getBaseExcelSmokeInfo(smokeInfoList);

        // -------------------------------------
        // 根据解析的 csv 的数据，查询数据库，对比一下结果 得到(新增的。修改的。删除的)
        Map<String, Object> excelListMap = smokeService.getExcelListMap(smokeInfoList);
        // -------------------------------------

        //  写数据到excel中  List<ExcelSmokeInfo>
        byte[] bytes1 = excelWrite.writeOrderInfoTemplate1(excelSmokeInfo);
        // todo: excel文件写入磁盘。
//        FileOutputStream fileOutputStream = null;
//        try {
//            fileOutputStream= new FileOutputStream("C:\\Users\\lenovo\\Desktop\\1\\1.xls");
//            fileOutputStream.write(bytes1);
//        } catch (IOException e) {
//            throw new RuntimeException(e);
//        }finally {
//            try {
//                fileOutputStream.close();
//            } catch (IOException e) {
//                throw new RuntimeException(e);
//            }
//        }

//        byte[] bytes1 = excelWrite.writeOrderInfoTemplate1(excelListMap, smokeSingleComputeInfoMap);
        // 然后转换成图片
        return smokeService.excelToPic(bytes1);
    }

    /**
     * 返回所有烟的信息的图片
     *
     * @return 返回图片的byte数组
     */
    @Override
    @MethodEventDescription(event = "use {{#1}} template get smoke list pic", isFillMethodArg = true, isShowAllEventCost = true)
    public byte[] getSmokeListPicByte(String templateType) {
        List<String> smokeIds = new ArrayList<>();
        List<SmokeDto> smokeDtos = new ArrayList<>();
        List<Smoke> collectSmoke = smokeService.list().stream()
                .peek(smoke -> smokeIds.add(smoke.getId()))
                .map(smoke -> {
                    if (smoke.getOrgIsCigar() == null) {
                        log.warn("{} 不确定雪茄类型", smoke.getProductName());
                        throw new RuntimeException("smoke Exception");
                    }
                    smoke.setOrgIsCigar("1".equals(smoke.getOrgIsCigar()) ? "是" : "否");
                    smoke.setOrgIsAbnormity(smoke.getOrgIsAbnormity().equals("1") ? "是" : "否");
                    return smoke;
                })
                .sorted((o1, o2) -> (int) (100 * (Double.parseDouble(o1.getWholeSalePrice()) - Double.parseDouble(o2.getWholeSalePrice()))))
                .collect(Collectors.toList());
        Map<String, Pic> collectPic = picService.list(new LambdaQueryWrapper<Pic>().in(Pic::getObjId, smokeIds).select(Pic::getId, Pic::getObjId, Pic::getImageType, Pic::getPicAddress)).stream().collect(Collectors.toMap(Pic::getObjId, pic -> pic));

        collectSmoke.forEach(smoke -> {
            SmokeDto smokeDto = new SmokeDto();
            BeanUtil.copyProperties(smoke, smokeDto);
            Pic pic = collectPic.get(smokeDto.getId());
            if (pic != null) {
                File file = fileStore.fetchFile(pic.getPicAddress() + "." + pic.getImageType());
                smokeDto.setPicFile(file);
            }
            smokeDtos.add(smokeDto);
        });
        byte[] resBytes = null;
        byte[] bytes = excelWrite.writeSmokeListInfo(templateType, smokeDtos);
        resBytes = smokeService.excelToPic(bytes);
        return resBytes;

    }

    /**
     * 将csv文件中数据进行提取 读取到内存中
     *
     * @param baseSmokeInfos csv文件的数据
     * @return 返回转换后数据集合
     */
    @Override
    @MethodEventDescription(event = "get smoke basic info")
    public List<ExcelSmokeInfo> getBaseExcelSmokeInfo(List<CSVSmokeInfo> baseSmokeInfos) {
        //搜集csv文件订单列表中的barcode值为一个集合。
        List<ExcelSmokeInfo> smokeInfos = new ArrayList<>();
        List<String> barCodeList = baseSmokeInfos
                .stream()
                .filter(csvSmokeInfo -> Integer.parseInt(csvSmokeInfo.getOrderNum()) != 0)
                .map(CSVSmokeInfo::getBarCode)
                .collect(Collectors.toList());
        Map<String, CSVSmokeInfo> csvSmokeInfoMap = baseSmokeInfos.stream().collect(Collectors.toMap(CSVSmokeInfo::getBarCode, csvSmokeInfo -> csvSmokeInfo));
        //将数据库 查询到的数据收集为一个map。  键为barcode。值为对象本身进行收集。
        Map<String, SmokeDto> smokeDtoBarcodeMap = smokeMapper.selectAllIsSaleSmokeByBarCode(barCodeList).stream().collect(Collectors.toMap(SmokeDto::getBarCode, smokeDto -> smokeDto));
        for (String barCode : barCodeList) {
            CSVSmokeInfo csvSmokeInfo = csvSmokeInfoMap.get(barCode);
            String orderNum = csvSmokeInfo.getOrderNum();

            SmokeDto smokeDto = smokeDtoBarcodeMap.get(barCode);

            if (smokeDto == null) {
                if (csvSmokeInfo.getShoppingName().equals("云烟（呼伦贝尔天堂草原）")) {
                    System.out.println(csvSmokeInfo + "暂时跳过");
                    continue;
                }
                throw new SmokeException(csvSmokeInfo.getShoppingName() + "条码缺失,可能需要更新数据，请联系管理员", CODE.SMOKE_BAR_CODE_IN_EXIST);
            }

            ExcelSmokeInfo smokeInfo = new ExcelSmokeInfo();
            String csvWhole = formatToTwoDecimals(csvSmokeInfo.getWholesalePrice());
            String csvSuggest = formatToTwoDecimals(csvSmokeInfo.getSuggestedSellPrice());

            String dataBaseWhole = smokeDto.getWholeSalePrice();
            String dataBaseSuggest = smokeDto.getRetailPrice();

            if (!csvWhole.equals(dataBaseWhole)) {
                log.error("csv文件中的批发价与数据库中的批发价不一致：" + "csv 数据为 :" + csvSmokeInfo + " 数据库数据为：" + smokeDto);
                throw new RuntimeException("csv文件中的批发价与数据库中的批发价不一致：" + "csv 数据为 :" + csvSmokeInfo + " 数据库数据为：" + smokeDto);
            }
            if (!csvSuggest.equals(dataBaseSuggest)) {
                log.error("csv文件中的建议售卖价与数据库中的建议售卖价不一致 csv 数据为 : {} 数据库数据为：{}", csvSmokeInfo, smokeDto);
                throw new RuntimeException("csv文件中的建议售卖价与数据库中的建议售卖价不一致：" + "csv 数据为 :" + csvSmokeInfo + " 数据库数据为：" + smokeDto);
            }
            smokeInfo.setName(smokeDto.getProductName());
            smokeInfo.setWholesalePrice(new DecimalFormat("#.##").format(Double.parseDouble(smokeDto.getWholeSalePrice())));
            smokeInfo.setOrderNum(orderNum);

            smokeInfo.setSuggestedSellPrice(smokeDto.getRetailPrice());
            smokeInfo.setBarCode(barCode);

            String realSalePriceBox = smokeDto.getRealSalePriceBox();
            String realSalePriceBar = smokeDto.getRealSalePriceBar();

            //真实售价
            if (realSalePriceBox == null || realSalePriceBar == null) {
                throw new SmokeException("实际售卖价缺失：" + smokeDto.getProductName(), CODE.SMOKE_INFO_DEFICIENCY);
            }

            BigDecimal realSalePriceDec = new BigDecimal(realSalePriceBar);
            BigDecimal wholeSalePriceDec = new BigDecimal(smokeDto.getWholeSalePrice());
            realSalePriceDec = realSalePriceDec.setScale(2, RoundingMode.HALF_UP);
            wholeSalePriceDec = wholeSalePriceDec.setScale(2, RoundingMode.HALF_UP);

            //单个商品合计。
            BigDecimal orderNumDecimal = new BigDecimal(orderNum);
            smokeInfo.setSinglePrice(orderNumDecimal.multiply(wholeSalePriceDec).setScale(2, RoundingMode.HALF_UP).toString());
            //计算单条！！利润
            BigDecimal barProfit = realSalePriceDec.subtract(wholeSalePriceDec);
            //设置显示的值
            smokeInfo.setRealSalePriceBox(realSalePriceBox);
            smokeInfo.setRealSalePriceBar(realSalePriceBar);
            smokeInfo.setEarnStr(orderNum + " * " + barProfit.toString() + " = " + new BigDecimal(orderNum).multiply(barProfit).toString());
            //需要四舍五入
            smokeInfo.setRealBarRate(new DecimalFormat("0.00%").format(barProfit.divide(realSalePriceDec, 4, RoundingMode.HALF_UP)));
            //设置不显示的值
            smokeInfo.setIsAbnormity(smokeDto.getOrgIsAbnormity());
            smokeInfo.setBarProfit(barProfit.toString());
            if (smokeDto.getPicAddress() != null) {
                File file = fileStore.fetchFile(smokeDto.getPicAddress() + "." + smokeDto.getImageType());
                smokeInfo.setImgFile(file);
            }
            smokeInfos.add(smokeInfo);
        }

        // 合并不到一起。
        List<ExcelSmokeInfo> sortedList = smokeInfos
                .stream()
                .sorted((o1, o2) -> (int) (100 * Double.parseDouble(o1.getWholesalePrice()) - 100 * Double.parseDouble(o2.getWholesalePrice())))
                .collect(Collectors.toList());

        List<ExcelSmokeInfo> addIds = IntStream.range(0, sortedList.size())
                .mapToObj(i -> {
                    ExcelSmokeInfo smokeInfo = sortedList.get(i);
                    smokeInfo.setId(i + 1 + "");
                    return smokeInfo;
                })
                .collect(Collectors.toList());
        return addIds;
    }

    /**
     * 返回有差距的信息  新增的，删除的，修改的，对数据的一个总结。在后端做总结分析吧，前端怪怪的。
     * 需要改进的方向还有很多，我还想知道是哪个字段进行了改进，
     *
     * @return 返回檢查結果
     */
    @Override
    public CheckInfoRes checkInfo() {
//      结果检查信息对象
        CheckInfoRes checkInfoRes = new CheckInfoRes();
//        checkInfoRes 所有的检查结果信息，前端也可以根据这个数据进行分析。
        List<CheckInfo> checkInfoList = new ArrayList<>();
//        新增的数据
        List<ProductBeanResult> addInfos = new ArrayList<>();
//        下架的数据
        List<SmokeInfoLog> deleteInfos = new ArrayList<>();
//        更新的数据
        List<CheckInfo> updateInfos = new ArrayList<>();
//        新数据的备注
        List<String> remarkList = new ArrayList<>();
//        新增和下架共同的数据合集
        List<String> addAndDeleteIds = new ArrayList<>();

        //查询  数据库信息 与 页面信息是否相同，如果不相同的话，存两个集合在
        // 网页数据
        List<ProductBeanResult> productBeanResultList = shanxiBaseClick.selectAllSmokeInfo().getRows();
        if (productBeanResultList == null || productBeanResultList.size() == 0) {
            return null;
        }
        //将原本的（） 括号给替换掉 ()
        for (ProductBeanResult productBeanResult : productBeanResultList) {
            productBeanResult.setProduct_name(StringUtils.replaceParenthesis(productBeanResult.getProduct_name()));
            productBeanResult.setShow_name(StringUtils.replaceParenthesis(productBeanResult.getShow_name()));
        }
        //对数据库查询到的数据库 数据进行map转换 将productUuid 作为key
        Map<String, SmokeInfoLog> smokeInfoLogMap = smokeInfoLogMapper.selectAllIsSaleDate().stream().collect(Collectors.toMap(SmokeInfoLog::getProductUuid, smokeInfoLog -> smokeInfoLog));
        // 比较 数据库数据和 网页的数据，判断上架，下架，修改的数据都有哪些。  两条数据根据 productUuid 进行对应  遍历的是网页数据
        for (ProductBeanResult productBeanResult : productBeanResultList) {
            String productUuid = productBeanResult.getProduct_uuid();
            SmokeInfoLog smokeInfoLog = smokeInfoLogMap.get(productUuid);
            // 进行逐条比较数据 查看查看是否 某些字段进行了修改。
            if (smokeInfoLog != null) {
                HashMap<ObjCompareFunction<Object, ProductBeanResult>, ObjCompareFunction<Object, SmokeInfoLog>> objCompareFunctionMap = new HashMap<>();
                objCompareFunctionMap.put(ProductBeanResult::getBrand_uuid, SmokeInfoLog::getBrandUuid);
                objCompareFunctionMap.put(ProductBeanResult::getFactory_uuid, SmokeInfoLog::getFactoryUuid);
                objCompareFunctionMap.put(ProductBeanResult::getOnsale, SmokeInfoLog::getOnsale);
                objCompareFunctionMap.put(ProductBeanResult::getProduct_code, SmokeInfoLog::getProductCode);
                objCompareFunctionMap.put(ProductBeanResult::getProduct_style_code, SmokeInfoLog::getProductStyleCode);
                objCompareFunctionMap.put(ProductBeanResult::getPrice_type_code, SmokeInfoLog::getPriceTypeCode);
                objCompareFunctionMap.put(ProductBeanResult::getTar_qty, SmokeInfoLog::getTarQty);
                objCompareFunctionMap.put(ProductBeanResult::getWhole_sale_price, SmokeInfoLog::getWholeSalePrice);
                objCompareFunctionMap.put(ProductBeanResult::getRetail_price, SmokeInfoLog::getRetailPrice);
                CompareRes compare = ObjComparators.compare(productBeanResult, smokeInfoLog, objCompareFunctionMap);
                //如果说比较结果是 false 说明不相等，就添加一个变更到  checkInfoList 中，然后移除这个数据，只要是能对的上就进行移除
                if (!compare.isaBoolean()) {
                    productBeanResult.setDataStatus('3');
                    CheckInfo checkInfo = new CheckInfo(smokeInfoLog, productBeanResult, compare.getDiffs());
                    checkInfoList.add(checkInfo);
                }
                //说明需要比较的都相等。 那么就认为 这一条是相等的。 在数据查到的数据进行移除这条数据
                smokeInfoLogMap.remove(productUuid);

            } else {
                //如果查询为空说明是新增的数据。
                productBeanResult.setDataStatus('1');
                checkInfoList.add(new CheckInfo(null, productBeanResult));
            }
        }
        //现在判断是否有数据没有被移除。 说明数据库有数据，但是网页没数据，这条数据已经被下架了，
        if (smokeInfoLogMap.size() != 0) {
            for (Map.Entry<String, SmokeInfoLog> smokeInfoLogEntry : smokeInfoLogMap.entrySet()) {
                SmokeInfoLog value = smokeInfoLogEntry.getValue();
                value.setDataStatus('2');
                checkInfoList.add(new CheckInfo(value, null));
            }
        }
        //分析这些数据，
        for (CheckInfo checkInfo : checkInfoList) {
            if (checkInfo.getOldInfo() == null && checkInfo.getNewInfo() != null) {
                addInfos.add(checkInfo.getNewInfo());
            }
            if (checkInfo.getOldInfo() != null && checkInfo.getNewInfo() == null) {
                deleteInfos.add(checkInfo.getOldInfo());
            }
            if (checkInfo.getOldInfo() != null && checkInfo.getNewInfo() != null) {
                updateInfos.add(checkInfo);
            }
        }
        //发现以下产品名称在“新增的产品”和“下架的产品”中同时出现：' + commonProductNames.join(', ')
        StringBuilder addAndRemarkRemark = new StringBuilder();
        // 为了打印一次  发现。。。的标志。
        boolean flag = false;
        Map<String, SmokeInfoLog> collect = deleteInfos.stream().collect(Collectors.toMap(SmokeInfoLog::getProductName, smokeInfoLog -> smokeInfoLog));

        for (ProductBeanResult addInfo : addInfos) {
            SmokeInfoLog smokeInfoLog = collect.get(addInfo.getProduct_name());
            String org_is_abnormity = addInfo.getOrg_is_abnormity();
            if (null == org_is_abnormity || org_is_abnormity.equals("")) {
                StringBuilder abnormityStr = new StringBuilder();
                abnormityStr.append(addInfo.getProduct_name() + "没有异型标志,请自行添加处理");
                remarkList.add(abnormityStr.toString());
            }
            if (smokeInfoLog != null) {
                addAndDeleteIds.add(smokeInfoLog.getProductUuid() + "_" + addInfo.getProduct_uuid());
                if (!flag) {
                    addAndRemarkRemark.append("发现以下产品名称在[新增产品]和[下架产品]中同时出现：");
                    flag = true;
                }
                addAndRemarkRemark.append(addInfo.getProduct_name()).append(" ");
            }
        }
        remarkList.add(addAndRemarkRemark.toString());

        checkInfoRes.setCheckInfos(checkInfoList);

        checkInfoRes.setAddInfos(addInfos);
        checkInfoRes.setDeleteInfos(deleteInfos);
        checkInfoRes.setUpdateInfos(updateInfos);
        checkInfoRes.setRemarks(remarkList);
        checkInfoRes.setAddAndDeleteIds(addAndDeleteIds);
        return checkInfoRes;
    }

    /**
     * 更新数据库
     * 查询放在了循环里面，不合适。
     *
     * @param checkInfoRes 之前查询的差异结果集
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public CheckInfoRes updateDatabase(CheckInfoRes checkInfoRes) {
        CheckInfoRes checkInfoResLasted = smokeService.checkInfo();
        //比较最新的数据和前端传递的数据的区别，比较add delete update 里面的数据
        List<ProductBeanResult> addInfosLasted = checkInfoResLasted.getAddInfos();
        List<SmokeInfoLog> deleteInfosLasted = checkInfoResLasted.getDeleteInfos();
        List<CheckInfo> updateInfosLasted = checkInfoResLasted.getUpdateInfos();
        //---
        List<ProductBeanResult> addInfos = checkInfoRes.getAddInfos();
        List<SmokeInfoLog> deleteInfos = checkInfoRes.getDeleteInfos();
        List<CheckInfo> updateInfos = checkInfoRes.getUpdateInfos();
        //重新查询一下 传入的差距和 现在的数据是否相同，如果不相同，可能是数据又发生变化了。所以就需要重新查询差异！！
        for (int i = 0; i < addInfos.size(); i++) {
            boolean compare = ObjComparators.compare(addInfos.get(i), addInfosLasted.get(i), ProductBeanResult::getProduct_uuid);
            if (!compare) {
                return null;
            }
        }
        for (int i = 0; i < deleteInfos.size(); i++) {
            boolean compare = ObjComparators.compare(deleteInfos.get(i), deleteInfosLasted.get(i), SmokeInfoLog::getProductUuid);
            if (!compare) {
                return null;
            }
        }
        for (int i = 0; i < updateInfos.size(); i++) {
            boolean compareOld = ObjComparators.compare(updateInfos.get(i).getOldInfo(), updateInfosLasted.get(i).getOldInfo(), SmokeInfoLog::getItemUniqueIndexId);
            boolean compareNew = ObjComparators.compare(updateInfos.get(i).getNewInfo(), updateInfosLasted.get(i).getNewInfo(), ProductBeanResult::getProduct_uuid);
            if (!compareOld || !compareNew) {
                return null;
            }
        }
        Map<String, ProductBeanResult> addMap = addInfosLasted.stream().collect(Collectors.toMap(ProductBeanResult::getProduct_uuid, item -> item));
        Map<String, SmokeInfoLog> deleteMap = deleteInfosLasted.stream().collect(Collectors.toMap(SmokeInfoLog::getProductUuid, item -> item));
        Map<String, CheckInfo> updateMap = updateInfos.stream().collect(Collectors.toMap(checkInfo -> checkInfo.getOldInfo().getProductUuid(), checkInfo -> checkInfo));

        //定义操作数据库的数据
        List<Smoke> addSmokeList = new ArrayList<>();
        List<SmokeInfoLog> addSmokeInfoLogList = new ArrayList<>();


        //开始更新！！
        //1.先把在 新增和下架的部分解决掉。并且在新增和下架 集合中 删除这条数据 在smkeinfolog添加这条数据更新数据的版本 在smoke添加这条数据，
        // 更新数据的版本。首先进行删除，然后进行新增
        List<String> addAndDeleteIds = checkInfoRes.getAddAndDeleteIds();
        //这种情况只可能是在原本有，现在也有，才会出现这种情况。不可能是原本无。现在也无
//        1.1 首先进行smokeInfoLog下架的操作  之后进行smoke下架的操作
//        中间进行一个休眠几毫秒都行，避免时间一样样的
//        1.2 之后进行SmokeInfoLogs上架的操作 之后进行Smoke上架的操作。
        for (String addAndDeleteId : addAndDeleteIds) {
            String[] product_uuid = addAndDeleteId.split("_");
            //下架smokeInfoLog
            SmokeInfoLog smokeInfoLog = deleteMap.get(product_uuid[0]);
            smokeInfoLog.setId(CUtil.getUUID());
            smokeInfoLog.setCreateId(ThreadUserInfo.get().getId());
            smokeInfoLog.setCreateTime(LocalDateTime.now());
            smokeInfoLog.setVersionInfo(smokeInfoLog.getVersionInfo() + 1);
            smokeInfoLog.setDataStatus('2');
//            smokeInfoLog.setBatchSerialId();
            addSmokeInfoLogList.add(smokeInfoLog);
            //下架smoke`
            String itemUniqueIndexId = smokeInfoLog.getItemUniqueIndexId();
            Smoke smoke = smokeMapper.selectOneLatestSmokeByItemId(itemUniqueIndexId);
            if (smoke == null) {
                throw new RuntimeException("查询不到产品对应的uuid");
            }
            smoke.setId(CUtil.getUUID());
            smoke.setCreateId(ThreadUserInfo.get().getId());
            smoke.setCreateTime(LocalDateTime.now());
            smoke.setVersionInfo(smoke.getVersionInfo() + 1);
            smoke.setDataStatus('2');
            addSmokeList.add(smoke);
            //上架smokeInfoLog
            ProductBeanResult productBeanResult = addMap.get(product_uuid[1]);
//            id itemId createId createTime datastatus versionInfo productUUID
            SmokeInfoLog smokeInfoLogConvert = ProductConvertSmokeInfoLog(productBeanResult);
            smokeInfoLogConvert.setId(CUtil.getUUID());
            smokeInfoLogConvert.setCreateId(ThreadUserInfo.get().getId());
            smokeInfoLogConvert.setCreateTime(LocalDateTime.now());
            smokeInfoLogConvert.setVersionInfo(smokeInfoLog.getVersionInfo() + 1);
            smokeInfoLogConvert.setItemUniqueIndexId(smokeInfoLog.getItemUniqueIndexId());
            smokeInfoLogConvert.setDataStatus('1');
            smokeInfoLogConvert.setProductUuid(productBeanResult.getProduct_uuid());
            addSmokeInfoLogList.add(smokeInfoLogConvert);
            //上架 smoke
            // 还已经修改的 未修改的 updateID updateId updateTime
            // itemId dataStatus  versionInfo createId createTime barcode realSalePriceBar  realSalePriceBox orgIsCigar
            Smoke smokeConvert = SmokeInfoLogConvertSmoke(smokeInfoLogConvert);
            smokeConvert.setId(CUtil.getUUID());
            smokeConvert.setDataStatus('1');
            smokeConvert.setCreateId(ThreadUserInfo.get().getId());
            smokeConvert.setVersionInfo(smoke.getVersionInfo() + 1);
            smokeConvert.setCreateTime(LocalDateTime.now());
            smokeConvert.setBarCode(smoke.getBarCode());
            smokeConvert.setRealSalePriceBox(smoke.getRealSalePriceBox());
            smokeConvert.setRealSalePriceBar(smoke.getRealSalePriceBar());
            smokeConvert.setOrgIsCigar(smoke.getOrgIsCigar());
            smokeConvert.setItemUniqueIndexId(smokeInfoLog.getItemUniqueIndexId());
            smokeConvert.setProductIsCorporation("1");
            addSmokeList.add(smokeConvert);
            //操作完之后在 map中移除 数据
            addMap.remove(product_uuid[1]);
            deleteMap.remove(product_uuid[0]);
        }
        //2.再解决新增， 可能上架的这个是之前存在的。之前下架过！！！但是现在又上架了，通过名字查询，名字查不到的话进行正常的新增。
        // 这事闹得，只能根据名字来修改。
        for (Map.Entry<String, ProductBeanResult> entry : addMap.entrySet()) {

            //在 smokeInfo新增。在smoke新增
            //id itemId createId createTime datastatus versionInfo
            SmokeInfoLog smokeInfoLog = ProductConvertSmokeInfoLog(entry.getValue());
            SmokeInfoLog smokeInfoLogDatabase = smokeInfoLogMapper.selectOneLatestSmokeByProductName(smokeInfoLog.getProductName());
            boolean databaseExistFlag = false;
            if (smokeInfoLogDatabase != null) {
                //如果不为空说明数据库原本存在这条数据，  如果状态为2，说明正常，层架下架了，现在如果不为2，说明不正常
                if (smokeInfoLogDatabase.getDataStatus() != '2') {
                    throw new RuntimeException("为什么新增的数据之前在数据库没下架" + smokeInfoLog.toString());
                }
                //如果等于2，说明已经下架了，现在进行新增的话需要沿用之前的id
                databaseExistFlag = true;
            }
            smokeInfoLog.setId(CUtil.getUUID());
            smokeInfoLog.setCreateId(ThreadUserInfo.get().getId());
            smokeInfoLog.setCreateTime(LocalDateTime.now());
            smokeInfoLog.setDataStatus('1');
            smokeInfoLog.setProductUuid(entry.getValue().getProduct_uuid());
            if (databaseExistFlag) {
                smokeInfoLog.setItemUniqueIndexId(smokeInfoLogDatabase.getItemUniqueIndexId());
                smokeInfoLog.setVersionInfo(smokeInfoLogDatabase.getVersionInfo() + 1);
            } else {
                smokeInfoLog.setVersionInfo(1);
                smokeInfoLog.setItemUniqueIndexId(CUtil.getUUID());
            }
            addSmokeInfoLogList.add(smokeInfoLog);
            //  在smoke新增
            //  realSalePriceBar  realSalePriceBox  这俩得自己填   barcode  orgIsCigar 这俩可以查 TODO：
            //  itemId dataStatus pro_is_corporation versionInfo createId createTime updateID updateId updateTime
            Smoke smoke = SmokeInfoLogConvertSmoke(smokeInfoLog);
            if (databaseExistFlag) {
                Smoke smoke1 = smokeMapper.selectOneLatestSmokeByProductName(smoke.getProductName());
                if (smoke1 != null) {
                    smoke.setVersionInfo(smoke1.getVersionInfo() + 1);
                    smoke.setItemUniqueIndexId(smoke1.getItemUniqueIndexId());
                    smoke.setBarCode(smoke1.getBarCode());
                    smoke.setRealSalePriceBox(smoke1.getRealSalePriceBox());
                    smoke.setRealSalePriceBar(smoke1.getRealSalePriceBar());
                    smoke.setOrgIsCigar(smoke1.getOrgIsCigar());
                } else {
                    throw new RuntimeException("有问题" + smoke.getProductName() + "数据为什么在smoke表不存在");
                }
            } else {
                smoke.setVersionInfo(1);
                smoke.setItemUniqueIndexId(smokeInfoLog.getItemUniqueIndexId());
            }
            smoke.setId(CUtil.getUUID());
            smoke.setProductUuid(smokeInfoLog.getProductUuid());
            smoke.setDataStatus('1');
            smoke.setProductIsCorporation("1");
            smoke.setCreateId(ThreadUserInfo.get().getId());
            smoke.setCreateTime(LocalDateTime.now());
            addSmokeList.add(smoke);
        }
        //3.在解决下架 。直接查询出来下架吧
        for (Map.Entry<String, SmokeInfoLog> smokeInfoLogEntry : deleteMap.entrySet()) {
            //两个都下架  smokeInfoLog  和 smoke都下架
            SmokeInfoLog smokeInfoLog = smokeInfoLogEntry.getValue();
            smokeInfoLog.setId(CUtil.getUUID());
            smokeInfoLog.setVersionInfo(smokeInfoLog.getVersionInfo() + 1);
            smokeInfoLog.setDataStatus('2');
            smokeInfoLog.setCreateTime(LocalDateTime.now());
            smokeInfoLog.setCreateId(ThreadUserInfo.get().getId());
            addSmokeInfoLogList.add(smokeInfoLog);
            String itemUniqueIndexId = smokeInfoLog.getItemUniqueIndexId();
            Smoke smoke = smokeMapper.selectOneLatestSmokeByItemId(itemUniqueIndexId);
            smoke.setDataStatus('2');
            smoke.setId(CUtil.getUUID());
            smoke.setVersionInfo(smoke.getVersionInfo() + 1);
            smoke.setCreateTime(LocalDateTime.now());
            smoke.setCreateId(ThreadUserInfo.get().getId());
            smoke.setProductIsCorporation("1");
            smoke.setUpdateId(null);
            smoke.setUpdateTime(null);
            addSmokeList.add(smoke);
        }
        //4.解决更新
        //需要更新两个地方。smoke 表和smokeInfoLog表。 需要之前的数据。
        for (Map.Entry<String, CheckInfo> checkInfoEntry : updateMap.entrySet()) {
            CheckInfo checkInfo = checkInfoEntry.getValue();
            ProductBeanResult productBeanResultNewInfo = checkInfo.getNewInfo();
            SmokeInfoLog smokeInfoLogOldInfo = checkInfo.getOldInfo();
            SmokeInfoLog smokeInfoLog = ProductConvertSmokeInfoLog(productBeanResultNewInfo);
            //缺少 id itemId uuid createId createTime datastatus versionInfo
            smokeInfoLog.setId(CUtil.getUUID());
            smokeInfoLog.setItemUniqueIndexId(smokeInfoLogOldInfo.getItemUniqueIndexId());
            smokeInfoLog.setProductUuid(smokeInfoLogOldInfo.getProductUuid());
            smokeInfoLog.setDataStatus('3');
            smokeInfoLog.setVersionInfo(smokeInfoLogOldInfo.getVersionInfo() + 1);
            smokeInfoLog.setCreateId(ThreadUserInfo.get().getId());
            smokeInfoLog.setCreateTime(LocalDateTime.now());
            addSmokeInfoLogList.add(smokeInfoLog);
            //添加更新smoke的数据
            //判断是否需要更新 smoke
            Smoke smoke = smokeMapper.selectOneLatestSmokeByItemId(smokeInfoLogOldInfo.getItemUniqueIndexId());
            HashMap<ObjCompareFunction<Object, Smoke>, ObjCompareFunction<Object, SmokeInfoLog>> compareSmokeSmokeInfoLogFields = new HashMap<>();
            compareSmokeSmokeInfoLogFields.put(Smoke::getProductName, SmokeInfoLog::getProductName);
            //这条数据 应该说不会 不相等，因为就是根据这条数据来的，
            compareSmokeSmokeInfoLogFields.put(Smoke::getProductUuid, SmokeInfoLog::getProductUuid);
            compareSmokeSmokeInfoLogFields.put(Smoke::getProductCode, SmokeInfoLog::getProductCode);
            compareSmokeSmokeInfoLogFields.put(Smoke::getBrandUuid, SmokeInfoLog::getBrandUuid);
            compareSmokeSmokeInfoLogFields.put(Smoke::getFactorySimpleName, SmokeInfoLog::getFactorySimpleName);
            compareSmokeSmokeInfoLogFields.put(Smoke::getWholeSalePrice, SmokeInfoLog::getWholeSalePrice);
            compareSmokeSmokeInfoLogFields.put(Smoke::getRetailPrice, SmokeInfoLog::getRetailPrice);
            compareSmokeSmokeInfoLogFields.put(Smoke::getOrgIsAbnormity, SmokeInfoLog::getOrgIsAbnormity);
            compareSmokeSmokeInfoLogFields.put(Smoke::getPriceTypeCode, SmokeInfoLog::getPriceTypeCode);
            compareSmokeSmokeInfoLogFields.put(Smoke::getPriceTypeCode, SmokeInfoLog::getPriceTypeCodename);
            CompareRes compare = ObjComparators.compare(smoke, smokeInfoLog, compareSmokeSmokeInfoLogFields);
            //如果不需要更新就不更新。继续下一条数据
            if (!compare.isaBoolean()) {
                continue;
            }
            //进行更新
            Smoke smokeConvert = SmokeInfoLogConvertSmoke(smokeInfoLog);
            // 已添加：itemID dataStatus pro_is_corporation versionInfo orgIsCigar  createId createTime barcode realSalePriceBar realSalePriceBox
            // 未添加： updateID updateId updateTime
            smokeConvert.setId(CUtil.getUUID());
            smokeConvert.setDataStatus('3');
            smokeConvert.setProductIsCorporation("1");
            smokeConvert.setVersionInfo(smoke.getVersionInfo() + 1);
            smokeConvert.setOrgIsCigar(smoke.getOrgIsCigar());
            smokeConvert.setCreateId(ThreadUserInfo.get().getId());
            smokeConvert.setCreateTime(LocalDateTime.now());
            smokeConvert.setBarCode(smoke.getBarCode());
            smokeConvert.setRealSalePriceBar(smoke.getRealSalePriceBar());
            smokeConvert.setRealSalePriceBox(smoke.getRealSalePriceBox());
            addSmokeList.add(smokeConvert);
        }
        String latestSerialId = smokeMapper.getLatestSerialId();
        String serialId = getSerialId(latestSerialId, checkInfoResLasted.getCheckInfos().size());

        addSmokeList.forEach(smoke -> smoke.setBatchSerialId(serialId));
        addSmokeInfoLogList.forEach(smokeInfoLog -> smokeInfoLog.setBatchSerialId(serialId));

        smokeInfoLogService.saveBatch(addSmokeInfoLogList);
        smokeService.saveBatch(addSmokeList);
        return checkInfoRes;
    }

    /**
     * 更新 更新数据的时候没有被更新的数据。
     * 有是否为雪茄  还有其中的条码  各个烟的图片
     * 可能这个接口订烟的时候才开放，不过可以大概写写
     */
    @Override
    public Map<String, List<Smoke>> updateOtherData() {
        OrderingInformation orderingInformation = shanxiBaseClick.selectAvailableAmount();
        List<ProdLimitInfos> prod_limit_infos = orderingInformation.getProd_limit_infos();
        Map<String, Smoke> smokeMap = smokeMapper.selectAllIsSaleSmoke().stream().collect(Collectors.toMap(Smoke::getProductUuid, smoke -> smoke));
        List<Smoke> originalSmokes = new ArrayList<>();
        List<Smoke> updatedSmokes = new ArrayList<>();
        for (ProdLimitInfos prod_limit_info : prod_limit_infos) {
            String product_uuid = prod_limit_info.getProduct_uuid();
            Smoke smoke = smokeMap.get(product_uuid);
            if (smoke == null) {
                throw new SmokeException("数据有问题，请联系管理员", CODE.SMOKE_fail1);
            }
            String barCode = smoke.getBarCode();
            String bar_code2 = prod_limit_info.getBar_code2();
            String is_cigar = prod_limit_info.getIs_cigar();
            String orgIsCigar = smoke.getOrgIsCigar();
            if (!bar_code2.equals(barCode) || !is_cigar.equals(orgIsCigar)) {
                Smoke originalSmoke = new Smoke();
                BeanUtils.copyProperties(smoke, originalSmoke);
                originalSmokes.add(originalSmoke);
                smoke.setBarCode(bar_code2);
                smoke.setOrgIsCigar(is_cigar);
                smoke.setCreateId(ThreadUserInfo.get().getId());
                smoke.setCreateTime(LocalDateTime.now());
                smoke.setId(CUtil.getUUID());
                smoke.setVersionInfo(smoke.getVersionInfo() + 1);
                updatedSmokes.add(smoke);
            }
        }
        boolean b = smokeService.saveBatch(updatedSmokes);
        Map<String, List<Smoke>> resMaps = new HashMap<>();
        resMaps.put("originalSmokes", originalSmokes);
        resMaps.put("updatedSmokes", updatedSmokes);
        if (b) {
            return resMaps;
        } else {
            throw new SmokeException("更新雪茄、barCode 失败", CODE.SMOKE_UPDATE_FAIL);
        }
    }

    /**
     * 下载缺失的图片
     *
     * @return
     */
    //smokeinfolog 表和 pic表连表查出没有下载的图片信息。  进行一个 下载。在数据库添加这条数据
    @Override
    public Map<String, List<String>> downDeficiencyPic() {
        Map<String, List<String>> resMap = new HashMap<>();
        //查询没有被下载的图片
        List<SmokeInfoLog> smokeInfoLogs = smokeInfoLogMapper.selectQueryImagesNotBeenDownloaded();
        //保存 时间 pic的集合
        List<Pic> pics = new ArrayList<>();
        List<String> downSuccessList = new ArrayList<>();
        List<String> downFailList = new ArrayList<>();
        //下载
        for (SmokeInfoLog smokeInfoLog : smokeInfoLogs) {
            String imgMain = smokeInfoLog.getImgMain();

            byte[] bytes = shanxiBaseClick.downHttpPic(imgMain);
            ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
            BufferedImage image = null;
            try {
                image = ImageIO.read(bis);
            } catch (IOException e) {
                throw new PicNullException();
            }
            if (image != null) {
                Pic pic = new Pic();
                pic.setId(CUtil.getUUID());
                pic.setCreateId("111");
                pic.setObjId(smokeInfoLog.getItemUniqueIndexId());
                pic.setImageType("jpg");
                String addressUuid = CUtil.getUUID();
                pic.setPicAddress(addressUuid);
                pic.setCreateTime(LocalDateTime.now());
                pics.add(pic);
                downSuccessList.add(smokeInfoLog.getProductName());
                // 保存图片到本地文件
                try {
                    ImageIO.write(image, "jpg", new File("src\\main\\resources\\smokePic\\" + addressUuid + ".jpg"));
                } catch (IOException e) {
                    e.printStackTrace();
                }
            } else {
                downFailList.add(smokeInfoLog.getProductName());
            }
            try {
                bis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        resMap.put("success", downSuccessList);
        resMap.put("fail", downFailList);
        if (picService.saveBatch(pics)) {
            return resMap;
        } else {
            return null;
        }
    }

    //查询数据库卷烟的差异。
    @Override
    public Map<String, List<SmokeNews>> queryLatelyAlertSmoke() {
        Map<String, List<SmokeNews>> changeMap = new HashMap<>();
        List<String> updateIds = new ArrayList<>();
        List<SmokeNews> addSmokeNews = new ArrayList<>();
        List<SmokeNews> deleteSmokeNews = new ArrayList<>();
        List<SmokeNews> updateSmokeNews = new ArrayList<>();
        List<Smoke> smokes = smokeMapper.queryLatelyAlertSmoke();
        for (Smoke smoke : smokes) {
            char dataStatus = smoke.getDataStatus();
            if (dataStatus == 1) {
                //上架
                SmokeDto smokeDto = new SmokeDto();
                BeanUtils.copyProperties(smoke, smokeDto);
                SmokeNews smokeNews = new SmokeNews();
                smokeNews.setSmokeDtoNew(smokeDto);
                addSmokeNews.add(smokeNews);
            } else if (dataStatus == 2) {
                //下架
                SmokeDto smokeDto = new SmokeDto();
                BeanUtils.copyProperties(smoke, smokeDto);
                SmokeNews smokeNews = new SmokeNews();
                smokeNews.setSmokeDtoOld(smokeDto);
                deleteSmokeNews.add(smokeNews);
            } else if (dataStatus == 3) {
                //在查一下之前 的版本，根据item_id
                updateIds.add(smoke.getItemUniqueIndexId());
            } else {
                throw new RuntimeException("一条数据怎么能没状态呢？");
            }
        }

//TODO:  查询的时候 有一个id不够，还需要一个版本号儿。当前版本-1、  上面查询烟的方式也不对， 会漏掉很多 本来就是新增的烟！！ 怎么做，当修改的时候加一个批次号。
// 看来得重新更新了，更新完之后看一下  那三个新增和下架的id对不对。。

        changeMap.put("addSmoke", addSmokeNews);
        changeMap.put("deleteSmoke", deleteSmokeNews);
        changeMap.put("update", updateSmokeNews);
        return changeMap;
    }

    @Override
    @MethodEventDescription(event = "excelToPic", isShowAllArg = true)
    public byte[] excelToPic(byte[] excelByte) {
        ByteArrayOutputStream byteArrayOutputStream = null;
        try {
            byteArrayOutputStream = FileUtil.excel2Pic(new ByteArrayInputStream(excelByte));
        } catch (IOException e) {
            e.printStackTrace();
        }
        return byteArrayOutputStream.toByteArray();
    }


    /**
     * 将 productBeanResult 属性转换到  SmokeInfoLog 中
     * <p>
     * smokeLog中缺少 id itemId productUuid createId createTime datastatus versionInfo
     *
     * @param productBeanResult
     * @return
     */
    public SmokeInfoLog ProductConvertSmokeInfoLog(ProductBeanResult productBeanResult) {
        SmokeInfoLog smokeInfoLog = new SmokeInfoLog();
        smokeInfoLog.setBrandCulture(productBeanResult.getBrand_culture());
        smokeInfoLog.setBrandName(productBeanResult.getBrand_name());
        smokeInfoLog.setBrandUuid(productBeanResult.getBrand_uuid());
        smokeInfoLog.setFactorySimpleName(productBeanResult.getFactory_simple_name());
        smokeInfoLog.setFactoryUuid(productBeanResult.getFactory_uuid());
        smokeInfoLog.setFeature(productBeanResult.getFeature());
        smokeInfoLog.setFlavour(productBeanResult.getFlavour());
        smokeInfoLog.setImgBack(productBeanResult.getImg_back());
        smokeInfoLog.setImgBottom(productBeanResult.getImg_bottom());
        smokeInfoLog.setImgCigarette(productBeanResult.getImg_cigarette());
        smokeInfoLog.setImgElevation(productBeanResult.getImg_elevation());
        smokeInfoLog.setImgFront(productBeanResult.getImg_front());
        smokeInfoLog.setImgLeft(productBeanResult.getImg_left());
        smokeInfoLog.setImgMain(productBeanResult.getImg_main());
        smokeInfoLog.setImgRight(productBeanResult.getImg_right());
        smokeInfoLog.setImgTop(productBeanResult.getImg_top());

        smokeInfoLog.setIsIntake(productBeanResult.getIs_intake());
        smokeInfoLog.setManageUnitUuid(productBeanResult.getManage_unit_uuid());
        smokeInfoLog.setOffTime(productBeanResult.getOff_time());
        smokeInfoLog.setOnsale(productBeanResult.getOnsale());
        smokeInfoLog.setOrgIsAbnormity(productBeanResult.getOrg_is_abnormity());
        smokeInfoLog.setPacking(productBeanResult.getPacking());
        smokeInfoLog.setPhotoFid(productBeanResult.getPhoto_fid());
        smokeInfoLog.setPriceTypeCode(productBeanResult.getPrice_type_code());
        smokeInfoLog.setPriceTypeCodename(productBeanResult.getPrice_type_codename());
        smokeInfoLog.setProductCode(productBeanResult.getProduct_code());
        smokeInfoLog.setProductCodeOrder(productBeanResult.getProduct_code_order());
        smokeInfoLog.setProductName(productBeanResult.getProduct_name());
        smokeInfoLog.setProductStyleCode(productBeanResult.getProduct_style_code());
        smokeInfoLog.setProductTypeCode(productBeanResult.getProduct_type_code());
        smokeInfoLog.setRemark(productBeanResult.getRemark());
        smokeInfoLog.setRetailPrice(productBeanResult.getRetail_price());
        smokeInfoLog.setShowName(productBeanResult.getShow_name());
        smokeInfoLog.setShowTag1(productBeanResult.getShow_tag1());
        smokeInfoLog.setShowTag2(productBeanResult.getShow_tag2());
        smokeInfoLog.setShowTag3(productBeanResult.getShow_tag3());
        smokeInfoLog.setSlogan(productBeanResult.getSlogan());
        smokeInfoLog.setSupplyFully(productBeanResult.getSupply_fully());
        smokeInfoLog.setTarQty(productBeanResult.getTar_qty());
        smokeInfoLog.setWholeSalePrice(productBeanResult.getWhole_sale_price());
        smokeInfoLog.setExtUuid(productBeanResult.getExt_uuid());
        smokeInfoLog.setOnTime(productBeanResult.getOn_time());
        smokeInfoLog.setPosition(productBeanResult.getPosition());
        smokeInfoLog.setProductTypeCodename(productBeanResult.getProduct_type_codename());
        return smokeInfoLog;
    }

    /**
     * smokeInfoLog 到Smoke的转换
     * 没有 id itemID productUUId barcode realSalePriceBar  realSalePriceBox orgIsCigar dataStatus pro_is_corporation versionInfo createId createTime updateID updateId updateTime
     *
     * @param smokeInfoLog
     * @return
     */
    public Smoke SmokeInfoLogConvertSmoke(SmokeInfoLog smokeInfoLog) {
        Smoke smoke = new Smoke();
        smoke.setProductName(smokeInfoLog.getProductName());
        smoke.setProductUuid(smokeInfoLog.getProductUuid());
        smoke.setBrandName(smokeInfoLog.getBrandName());
        smoke.setBrandUuid(smokeInfoLog.getBrandUuid());
        smoke.setProductCode(smokeInfoLog.getProductCode());
        smoke.setFactorySimpleName(smokeInfoLog.getFactorySimpleName());
        smoke.setWholeSalePrice(smokeInfoLog.getWholeSalePrice());
        smoke.setRetailPrice(smokeInfoLog.getRetailPrice());
        smoke.setOrgIsAbnormity(smokeInfoLog.getOrgIsAbnormity());
        smoke.setPriceTypeCodeName(smokeInfoLog.getPriceTypeCodename());
        smoke.setPriceTypeCode(smokeInfoLog.getPriceTypeCode());
        smoke.setProductIsSale(smokeInfoLog.getOnsale());
        smoke.setProductIsCorporation("1");
        return smoke;
    }

    /**
     * 得到序列号 批次数量
     */
    /**
     * @param maxSerialId          查询到之前的最大批次号
     * @param currentBatchQuantity 当前批次的数量
     * @return
     */
    public String getSerialId(String maxSerialId, int currentBatchQuantity) {
        if (currentBatchQuantity <= 0 || currentBatchQuantity > 100000000) {
            throw new RuntimeException("批次数量过多，大概率是被攻击了");
        }
        String currentDate = new SimpleDateFormat("yyyyMMdd").format(new Date());
        StringBuilder stringBuilder = new StringBuilder(currentDate);
        if (maxSerialId == null || !maxSerialId.startsWith(currentDate)) {
            stringBuilder.append("0001");
        } else {
            int batchNumber = Integer.parseInt(maxSerialId.substring(8, 12)) + 1;
            stringBuilder.append(String.format("%04d", batchNumber));
        }

        stringBuilder.append(ThreadUserInfo.get().getId().equals("111") ? "8888" : "9999");
        stringBuilder.append(String.format("%08d", currentBatchQuantity));
        stringBuilder.append(CUtil.getUUID(), 0, 8);
        return stringBuilder.toString();
    }

    //   修改得到的数据，进行一个阉割。这个阉割不需要监听器去阉割，而是后续的手段进行阉割  修正数据
    private void revisedData(WeeklySupplyStrategyData weeklySupplyStrategyData) {
        // 移除 gearPutList 中不需要的元素
        weeklySupplyStrategyData.getGearSelection().getList().removeIf(item -> !item.getReleaseOrNo().equals("是"));

        // 使用迭代器移除 gearExtendSelectionList 中不需要的元素
        Iterator<GearExtendSelection> gearExtendSelectionIterator = weeklySupplyStrategyData.getGearExtendSelectionList().iterator();
        while (gearExtendSelectionIterator.hasNext()) {
            GearExtendSelection gearExtendSelection = gearExtendSelectionIterator.next();
            List<AnticipatedOrderData> list = gearExtendSelection.getList();
            if (list != null) {
                list.removeIf(anticipatedOrderData -> !anticipatedOrderData.getReleaseOrNo().equals("是"));
                // 如果列表为空，则移除 gearExtendSelection
                if (list.isEmpty()) {
                    gearExtendSelectionIterator.remove();
                }
            }
        }
    }

    /**
     * 格式化数字字符串为两位小数
     *
     * @param numberStr 数字字符串
     * @return 格式化后的两位小数字符串
     */
    private String formatToTwoDecimals(String numberStr) {
        try {
            // 1. 移除字符串中的空格
            numberStr = numberStr.trim();

            // 2. 将字符串转换为double
            double number = Double.parseDouble(numberStr);

            // 3. 使用DecimalFormat格式化为两位小数
            DecimalFormat df = new DecimalFormat("0.00");
            df.setRoundingMode(RoundingMode.HALF_UP); // 设置四舍五入模式

            return df.format(number);
        } catch (Exception e) {
            System.err.println("数字格式化错误: " + numberStr);
            return numberStr; // 如果转换失败，返回原字符串
        }
    }
}